/*
 * @filename ExceptionHandler.java
 * @author barry
 * @version 0.0.1
 * @date 2020年3月12日
 */
package com.bnzj.core.webmvc.exception;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ValidationException;

import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.http.HttpStatus;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import com.bnzj.core.config.CoreProperties;
import com.bnzj.core.exception.BusinessException;
import com.bnzj.core.rest.ResponseResult;
import com.bnzj.core.rest.ResultCode;
import com.bnzj.core.util.FbConstant;
import com.bnzj.core.util.LogUtils;

import lombok.extern.slf4j.Slf4j;

//@ExceptionHandler：统一处理某一类异常，从而能够减少代码重复率和复杂度
//@ControllerAdvice：异常集中处理，更好的使业务逻辑与异常处理剥离开
//@ResponseStatus：可以将某种异常映射为HTTP状态码
/**
 * 全局异常处理器
 * @author barry
 * @date 2020-03-12
 */
@Slf4j
@RestControllerAdvice
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class GlobalExceptionHandler {
    
    @Resource
    private CoreProperties coreProperties;
    
    private void printLog(String uri, Exception e) {
        log.error(FbConstant.LINE_SEPARATOR + "---" + Thread.currentThread().getStackTrace()[2].toString());
        if (coreProperties.isDeveloperMode()) {
            log.error(uri, e);
        } else {
            LogUtils.error(log, uri, e, 20);
        }
    }

    /**
     * 处理其他未处理的异常
     * @param req
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(value = Exception.class)
    public ResponseResult<?> defaultErrorHandler(HttpServletRequest req, Exception e) {
        printLog(req.getRequestURI(), e);
        return ResponseResult.failure(ResultCode.UNKNOWN, e.getMessage());
    }
    
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(value = AsyncRequestTimeoutException.class)
    public void asyncRequestTimeoutExceptionHandler(HttpServletRequest req, AsyncRequestTimeoutException e) {
        printLog(req.getRequestURI(), e);
    }
    
    /**
     * 处理未知请求
     * @param req
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public ResponseResult<?> httpRequestMethodNotSupportedExceptionHandler(HttpServletRequest req, HttpRequestMethodNotSupportedException e) {
        printLog(req.getRequestURI(), e);
        return ResponseResult.failure(ResultCode.NOT_FOUND, e.getMessage());
    }
    
    /**
     * 处理数据校验异常
     * @param req
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
    @ExceptionHandler(value = {ValidationException.class, MethodArgumentNotValidException.class, MethodArgumentTypeMismatchException.class})
    public ResponseResult<?> handleMethodArgumentNotValidException(HttpServletRequest req, Exception e) {
        printLog(req.getRequestURI(), e);
        return ResponseResult.failure(ResultCode.BAD_REQUEST, e.getMessage());
    }
    
    /**
     * 处理业务异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = BusinessException.class)
    public ResponseResult<?> handleBusinessException(HttpServletRequest req, BusinessException e) {
        printLog(req.getRequestURI(), e);
        return ResponseResult.bizException(e);
    }
}